package cate.game.pvp.eliterace.handler;

import cate.common.table.d.GDEliteRace;
import cate.common.table.pvp.eliterace.EliteRaceRedPackageRow;
import cate.game.GameBody;
import cate.game.framework.common.compress.CompressedContent;
import cate.game.framework.common.compress.PlaySideCompressHelper;
import cate.game.play.part.PlaySide;
import cate.game.pvp.eliterace.data.*;
import cate.game.role.RoleSnapshot;
import cate.game.role.pvp.eliterace.msg.*;
import cate.game.role.pvp.eliterace.vo.EliteRaceGroupRoundVO;
import cate.game.role.pvp.eliterace.vo.EliteRaceReportVO;
import cate.game.role.pvp.eliterace.vo.EliteRaceRoundVO;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import easy.java.practice.errorcode.EcResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.function.Supplier;

@Slf4j
public class BothEliteRaceMessageHandler implements EliteRaceMessageHandler {

    private final EliteRaceRecordOperation recordOperation;
    private final EliteRacePsOperation psOperation;
    private final EliteRaceBetOperation betOperation;
    private final EliteRaceRedPacketOperation redPacketOperation;
    private final EliteRaceRankRwdOperation rankRwdOperation;

    public BothEliteRaceMessageHandler(Supplier<EliteRaceRecordOperation> recordOperationSupplier,
                                       Supplier<EliteRacePsOperation> psOperationSupplier,
                                       Supplier<EliteRaceBetOperation> betOperationSupplier,
                                       Supplier<EliteRaceRedPacketOperation> redPacketOperationSupplier,
                                       Supplier<EliteRaceRankRwdOperation> rankRwdOperationSupplier) {
        this.recordOperation = Objects.requireNonNull(recordOperationSupplier.get());
        this.psOperation = Objects.requireNonNull(psOperationSupplier.get());
        this.betOperation = Objects.requireNonNull(betOperationSupplier.get());
        this.redPacketOperation = Objects.requireNonNull(redPacketOperationSupplier.get());
        this.rankRwdOperation = Objects.requireNonNull(rankRwdOperationSupplier.get());
    }

    @Override
    public EcResult<EliteRaceBetRoundResp> handleBetRoundReq(EliteRaceBetRoundReq req) {
        EcResult<EliteRaceBetRoundResp> r = new EcResult<>();
        List<EliteRaceRecord> queryList = recordOperation.query(req.from, req.to);
        if (Objects.isNull(queryList)) {
            return r.fail();
        }

        Map<EliteRaceSpec, EliteRaceRoundVO> roundMap = Maps.newHashMap();

        queryList.stream()
                .filter(EliteRaceRecord::isBetRound)
                .map(this::mapToRoundVO)
                .forEach(entry -> roundMap.put(entry.getKey(), entry.getValue()));


        r.data = new EliteRaceBetRoundResp(roundMap);

        return r.success();
    }

    @Override
    public EcResult<EliteRaceBetRoundReportResp> handleBetRoundReportReq(EliteRaceBetRoundReportReq req) {
        EcResult<EliteRaceBetRoundReportResp> r = new EcResult<>();
        List<EliteRaceRecord> queryList = recordOperation.query(req.from, req.to);
        if (Objects.isNull(queryList)) {
            return r.fail();
        }

        Map<EliteRaceSpec, EliteRaceReportVO> reportMap = Maps.newHashMap();

        queryList.stream()
                .filter(EliteRaceRecord::isBetRound)
                .map(this::mapToReportVO)
                .forEach(entry -> reportMap.put(entry.getKey(), entry.getValue()));

        r.data = new EliteRaceBetRoundReportResp(reportMap);
        return r.success();
    }

    @Override
    public boolean hasRecvdRedPacket(String uid, EliteRaceSpec spec) {
        return redPacketOperation.query(spec, uid) != null;
    }

    @Override
    public EcResult<EliteRaceRoundResp> handleRoundReq(String uid, EliteRaceRoundReq req) {
        EcResult<EliteRaceRoundResp> r = new EcResult<>();
        List<EliteRaceRecord> queryList = recordOperation.query(uid, req.from, req.to);
        if (Objects.isNull(queryList)) {
            return r.fail();
        }
        Map<EliteRaceSpec, EliteRaceRoundVO> roundMap = Maps.newHashMap();

        queryList.stream()
                .map(this::mapToRoundVO)
                .forEach(entry -> roundMap.put(entry.getKey(), entry.getValue()));


        r.data = new EliteRaceRoundResp(roundMap);
        return r.success();
    }

    @Override
    public EcResult<EliteRaceRoundReportResp> handleRoundReportReq(String uid, EliteRaceRoundReportReq req) {
        EcResult<EliteRaceRoundReportResp> r = new EcResult<>();
        List<EliteRaceRecord> queryList = recordOperation.query(uid, req.from, req.to);
        if (Objects.isNull(queryList)) {
            return r.fail();
        }

        Map<EliteRaceSpec, EliteRaceReportVO> reportMap = Maps.newHashMap();

        queryList.stream()
                .map(this::mapToReportVO)
                .forEach(entry -> reportMap.put(entry.getKey(), entry.getValue()));


        r.data = new EliteRaceRoundReportResp(reportMap);

        return r.success();
    }

    @Override
    public EcResult<EliteRaceGroupRoundResp> handleGroupRoundReq(EliteRaceGroupRoundReq req) {
        EcResult<EliteRaceGroupRoundResp> r = new EcResult<>();

        if (Objects.isNull(req.to)) {
            return r.fail("参数错误!");
        }

        if (Objects.nonNull(req.from) && req.from.getGroup() != req.to.getGroup()) {
            return r.fail("参数GROUP必须一致");
        }

        List<EliteRaceRecord> queryList = recordOperation.query(req.from, req.to);

        if (Objects.isNull(queryList)) {
            return r.fail("查不到数据");
        }

        Map<EliteRaceSpec, List<EliteRaceGroupRoundVO>> roundMap = Maps.newHashMap();

        queryList.stream()
                .filter(rd -> rd.getSpec().getGroup() == req.to.getGroup())
                .map(this::mapToGroupRoundVO)
                .forEach(entry -> roundMap.computeIfAbsent(entry.getKey(),
                        k -> Lists.newArrayList()).add(entry.getValue()));

        r.data = new EliteRaceGroupRoundResp(req.tag, roundMap);

        return r.success();
    }

    @Override
    public EcResult<EliteRaceGroupRoundReportResp> handleGroupRoundReportReq(EliteRaceGroupRoundReportReq req) {
        EcResult<EliteRaceGroupRoundReportResp> r = new EcResult<>();

        if (Objects.isNull(req.to)) {
            return r.fail("参数错误!");
        }

        if (Objects.nonNull(req.from) && req.from.getGroup() != req.to.getGroup()) {
            return r.fail("参数GROUP必须一致");
        }

        List<EliteRaceRecord> queryList = recordOperation.query(req.from, req.to);

        if (Objects.isNull(queryList)) {
            return r.fail("查不到数据");
        }

        Map<EliteRaceSpec, List<EliteRaceReportVO>> reportMap = Maps.newHashMap();

        queryList.stream()
                .filter(rd -> rd.getSpec().getGroup() == req.to.getGroup())
                .map(this::mapToReportVO)
                .forEach(entry -> reportMap.computeIfAbsent(entry.getKey(),
                        k -> Lists.newArrayList()).add(entry.getValue()));

        r.data = new EliteRaceGroupRoundReportResp(req.tag, reportMap);

        return r.success();
    }

    @Override
    public EcResult<EliteRaceGetPsResp> handleGetPsReq(EliteRaceGetPsReq req) {
        EcResult<EliteRaceGetPsResp> r = new EcResult<>();

        if (!StringUtils.hasLength(req.uid)) {
            return r.fail("参数错误!");
        }

        EliteRacePs ps = psOperation.findOne(req.uid);
        if (Objects.isNull(ps)) {
            return r.fail("找不到玩家阵容!");
        }

        CompressedContent last = ps.getCpsList().getLast();
        EcResult<PlaySide> pr = PlaySideCompressHelper.uncompressEc(last);
        if (!pr.ok()) {
            return r.fail("解压玩家阵容出错!");
        }

        r.data = new EliteRaceGetPsResp(pr.data);

        return r.success();
    }

    @Override
    public EcResult<List<EliteRaceOpponent>> handleQueryPs(int srvId) {
        EcResult<List<EliteRaceOpponent>> r = new EcResult<>();
        if (srvId < 0) {
            return r.fail("参数错误!");
        }

        r.data = psOperation.query(srvId);

        return r;
    }

    @Override
    public EcResult<EliteRaceBetRecord> handleBetReq(EliteRaceSpec redPacketSpec, EliteRaceBetReq req) {
        EcResult<EliteRaceBetRecord> r = new EcResult<>();
        EliteRaceRedPacketRecord raceRedPacketRecord = redPacketOperation.query(redPacketSpec, req.uid);
        if (raceRedPacketRecord == null) {
            return r.fail("您没有足够的竞猜银币，请领取竞猜红包获取");
        }
        EliteRaceSpec spec = EliteRaceSpec.of(
                redPacketSpec.getSeason(),
                redPacketSpec.getPeriod(),
                redPacketSpec.getGroup(),
                redPacketSpec.getRound()
        );
        List<EliteRaceRecord> queryList = recordOperation.query(spec);
        if (CollectionUtils.isEmpty(queryList)) {
            return r.fail("找不到竞猜对应的比赛!");
        }

        EliteRaceBetRecord betRecord = this.betOperation.query(spec);
        if (Objects.isNull(betRecord)) {
            return r.fail("找不到竞猜对应的记录!");
        }

        EliteRaceRecord record = queryList.stream()
                .filter(rd -> rd.getSpec().getPeriod() == spec.getPeriod()
                        && rd.getSpec().getRound() == spec.getRound())
                .findFirst()
                .orElse(null);
        if (Objects.isNull(record)) {
            return r.fail("找不到竞猜对应的比赛!");
        }

        this.betOperation.bet(spec, req.side, GDEliteRace.FIXED_BET_NUM);
        r.data = betRecord;

        return r.success();
    }

    @Override
    public EcResult<EliteRaceBetRecord> handleGetBetRecord(EliteRaceSpec spec) {
        EcResult<EliteRaceBetRecord> r = new EcResult<>();
        EliteRaceBetRecord query = this.betOperation.query(spec);
        if (Objects.isNull(query)) {
            return r.fail("找不到对应的竞猜记录");
        }
        r.data = query;
        return r.success();
    }

    @Override
    public EcResult<List<EliteRaceRedPacketRecord>> handleGetRedPacket(RoleSnapshot snapshot, EliteRaceSpec spec, GameBody game) {
        EcResult<List<EliteRaceRedPacketRecord>> r = new EcResult<>();
        EliteRaceRedPacketRecord record = redPacketOperation.query(spec, snapshot.base.uid);
        if (Objects.isNull(record)) {
            record = EliteRaceRedPacketRecord.of(spec, snapshot, GDEliteRace.FIXED_BET_NUM);
            redPacketOperation.insert(spec, record);
        }
        List<EliteRaceRedPacketRecord> queryList = redPacketOperation.query(spec, 10);
        boolean b = queryList.stream().anyMatch(rd -> StringUtils.endsWithIgnoreCase(snapshot.base.uid, rd.getUid()));
        if (!b) {
            queryList.add(queryList.size() - 1, record);
        }
        r.data = queryList;
        return r;
    }

    @Override
    public EcResult<List<EliteRaceRedPacketRecord>> handleQueryRedPacket(EliteRaceSpec spec, String uid) {
        EcResult<List<EliteRaceRedPacketRecord>> r = new EcResult<>();
        EliteRaceRedPacketRecord record = redPacketOperation.query(spec, uid);
        List<EliteRaceRedPacketRecord> queryList = redPacketOperation.query(spec, 10);
        if (Objects.isNull(queryList)) {
            return r;
        }
        boolean b = queryList.stream().anyMatch(rd -> StringUtils.endsWithIgnoreCase(uid, rd.getUid()));
        if (!b && Objects.nonNull(record)) {
            queryList.add(queryList.size() - 1, record);
        }
        r.data = queryList;
        return r;
    }

    @Override
    public EcResult<List<EliteRaceRankRwd>> handleQueryRankRwd(EliteRaceRankRwdOperation.QueryParam param) {
        EcResult<List<EliteRaceRankRwd>> r = new EcResult<>();
        List<EliteRaceRankRwd> queryList = rankRwdOperation.query(param);
        if (CollectionUtils.isEmpty(queryList)) {
            return r.fail();
        }
        r.data = queryList;
        return r;
    }

    @Override
    public EcResult<Map<String, String>> handleQueryPlayers(int srvId, EliteRaceSpec spec) {
        EcResult<Map<String, String>> r = new EcResult<>();
        List<EliteRaceRecord> queryList;
        if (spec.getPeriod() == (byte) 2) {
            queryList = Lists.newArrayList();
            for (int i = 0; i < 8; i++) {
                EliteRaceSpec querySpec = EliteRaceSpec.of(
                        spec.getSeason(),
                        spec.getPeriod(), (byte) i,
                        spec.getRound(),
                        spec.getStep());
                List<EliteRaceRecord> recordList = recordOperation.query(querySpec);
                if (!CollectionUtils.isEmpty(recordList)) {
                    queryList.addAll(recordList);
                }
            }
        } else {
            queryList = recordOperation.query(spec);
        }
        if (CollectionUtils.isEmpty(queryList)) {
            return r.fail();
        }

        Map<String, String> map = Maps.newHashMap();
        for (EliteRaceRecord record : queryList) {
            if (StringUtils.hasLength(record.getIdA())) {
                EliteRacePs psA = psOperation.findOne(record.getIdA());
                if (Objects.nonNull(psA) && psA.getOpponent().getSnapshot().base.srvId == srvId) {
                    map.put(record.getIdA(), psA.getCpsList().getLast().getMd5Sum());
                }
            }

            if (StringUtils.hasLength(record.getIdB())) {
                EliteRacePs psB = psOperation.findOne(record.getIdB());
                if (Objects.nonNull(psB) && psB.getOpponent().getSnapshot().base.srvId == srvId) {
                    map.put(record.getIdB(), psB.getCpsList().getLast().getMd5Sum());
                }
            }
        }

        r.data = map;

        return r;
    }

    @Override
    public EcResult<Void> handleUpdatePs(EliteRaceSpec spec, EliteRacePs eps) {
        EcResult<Void> r = new EcResult<>();

        if (Objects.isNull(spec) || Objects.isNull(eps)) {
            return r.fail("参数错误");
        }

        psOperation.update(eps);

        return r;
    }

    @Override
    public EcResult<List<EliteRaceBetRecord>> handleQueryAll() {
        EcResult<List<EliteRaceBetRecord>> r = new EcResult<>();
        List<EliteRaceBetRecord> recordList = betOperation.all();
        r.data = recordList;
        return r;
    }

    private Map.Entry<EliteRaceSpec, EliteRaceReportVO> mapToReportVO(EliteRaceRecord record) {
        EliteRaceReportVO reportVO = EliteRaceReportVO.of(record);
        return new AbstractMap.SimpleEntry<>(record.getSpec(), reportVO);
    }

    private Map.Entry<EliteRaceSpec, EliteRaceRoundVO> mapToRoundVO(EliteRaceRecord record) {
        EliteRaceSpec spec = record.getSpec();

        EliteRacePs epsA = null, epsB = null;
        if (StringUtils.hasLength(record.getIdA())) {
            epsA = psOperation.findOne(record.getIdA());
        }
        if (StringUtils.hasLength(record.getIdB())) {
            epsB = psOperation.findOne(record.getIdB());
        }


        PlaySide psA = null, psB = null;

        if (Objects.nonNull(epsA)) {
            CompressedContent cc = epsA.getCpsList().get(record.getPsA());
            EcResult<PlaySide> r = PlaySideCompressHelper.uncompressEc(cc);
            if (r.ok()) {
                psA = r.data;
            }
        }

        if (Objects.nonNull(epsB)) {
            CompressedContent cc = epsB.getCpsList().get(record.getPsB());
            EcResult<PlaySide> r = PlaySideCompressHelper.uncompressEc(cc);
            if (r.ok()) {
                psB = r.data;
            }
        }

        EliteRaceRoundVO roundVO = EliteRaceRoundVO.of(record.getRid(), record.getIndex(), psA, psB);
        return new AbstractMap.SimpleEntry<>(spec, roundVO);
    }

    private Map.Entry<EliteRaceSpec, EliteRaceGroupRoundVO> mapToGroupRoundVO(EliteRaceRecord record) {
        EliteRaceSpec spec = record.getSpec();

        EliteRacePs epsA = null, epsB = null;
        if (StringUtils.hasLength(record.getIdA())) {
            epsA = psOperation.findOne(record.getIdA());
        }
        if (StringUtils.hasLength(record.getIdB())) {
            epsB = psOperation.findOne(record.getIdB());
        }


        RoleSnapshot snapshotA = null, snapshotB = null;

        if (Objects.nonNull(epsA)) {
            CompressedContent cc = epsA.getCpsList().get(record.getPsA());
            EcResult<PlaySide> r = PlaySideCompressHelper.uncompressEc(cc);
            if (r.ok()) {
                snapshotA = r.data.snapshot.copy();
            }
        }

        if (Objects.nonNull(epsB)) {
            CompressedContent cc = epsB.getCpsList().get(record.getPsB());
            EcResult<PlaySide> r = PlaySideCompressHelper.uncompressEc(cc);
            if (r.ok()) {
                snapshotB = r.data.snapshot.copy();
            }
        }

        EliteRaceGroupRoundVO roundVO = EliteRaceGroupRoundVO.of(
                record.getRid(),
                record.getIndex(),
                snapshotA,
                snapshotB);
        return new AbstractMap.SimpleEntry<>(spec, roundVO);
    }
}
